package subumbo.audio.core
{
	import __AS3__.vec.Vector;
	
	import flash.utils.Dictionary;
	
	import subumbo.audio.driver.AudioDriver;
	
	public class AudioEngine 
	{
		private var _driver: AudioDriver;;
		private var _mixer: Mixer;
		
		private var _processQue: Vector.<IAudioProcessor>;
		
		private var _generators:Dictionary;
		private var _channelEffects:Dictionary;
		private var _masterEffects:Dictionary;
		
		private var _debugFunction:Function;
		
		public var debug:Object = {};
		
		private var _mixIsDirty:Boolean;

		static public function get instance():AudioEngine
		{
			if(!_instance) _instance = new AudioEngine()
			return _instance;
		}
		static private var _instance:AudioEngine; 
		public function AudioEngine()
		{	
			_generators = new Dictionary();
			_channelEffects = new Dictionary();
			_masterEffects = new Dictionary();
			_processQue = new Vector.<IAudioProcessor>();
			_mixer = new Mixer(_processQue);	
			_driver = new AudioDriver( this );
			
			debug.generators = _generators;
			debug.channelEffects = _channelEffects;
			debug.masterEffects = _masterEffects;
			debug.processQue = _processQue;
			debug.numGenerators = 0;
			debug.numChannelEffects = 0;
		}
		
		
		/**
		 * 
		 * @param buffer Audio buffer
		 * 
		 */		
		public function process( buffer: Vector.<Vector.<Number>> ): void
		{
			if(_mixIsDirty) { _mixIsDirty = false; _mixer.cleanUp(_channelEffects, _generators);  _debugFunction.call(null, debug) }
			_mixer.process( buffer );	
		}



		public function addEffect( effect: IAudioProcessor): void
		{
			_masterEffects[effect.id] = effect;
		}
		public function removeEffect(obj:Object):void
		{
			
		}
		public function activateEffect( obj:Object ):void
		{
			
		}
		public function deactivateEffect( obj:Object ):void
		{
			
		}
		
		
		
		
		public function addChannelEffect (effect:IAudioProcessor, generatorID:String) :void
		{
			if(_channelEffects[generatorID][effect.id]) return;
			effect.processIndex = _generators[generatorID].processIndex;
			effect.activateFunction = activateChannelEffect;
			effect.deactivatedFunction = deactivateChannelEffect;
			 _channelEffects[generatorID][effect.id] = effect;
			 _mixIsDirty = true;
		}
		public function removeChannelEffect(obj:Object, generatorID:String):void
		{
			if(obj is String) 
			{
				if(_channelEffects[generatorID][obj])
				{
					obj = _channelEffects[generatorID][obj];			
				}else
				{
					return;
				}
			}
			
			deactivateChannelEffect(obj, generatorID);
			delete _channelEffects[generatorID][obj.id];
		}
		public function activateChannelEffect(obj:Object, generatorID:String):void
		{
			if(obj is String) 
			{
				if(_channelEffects[generatorID][obj])
				{
					obj = _channelEffects[generatorID][obj]			
				}else
				{
					return;
				}
			}
			
			if(_processQue.indexOf(obj) != -1) return;
			
			//if(!_channelEffects[generatorID][obj.id])  _channelEffects[generatorID][obj.id] = obj;
			obj.setActivated(true);
			var gen:IAudioProcessor = _generators[generatorID];
			Object(gen).numEffects++;
			var index:int = _processQue.indexOf(gen);
			if(index == -1) return;
			var i:int = 0;
			for each (var value:Object in _channelEffects[gen.id]) {
			  if(value.activated) i++;
			}
			_processQue.splice(index+i,0,obj);
			_mixIsDirty = true; 
		}
		public function deactivateChannelEffect(obj:Object, generatorID:String = null):void
		{
			if(obj is String) 
			{
				if(_channelEffects[generatorID][obj])
				{
					obj = _channelEffects[generatorID][obj]			
				}else
				{
					return;
				}
			}
			obj.setActivated(false);
			if(_generators[generatorID])_generators[generatorID].numEffects--;
			var index:int = _processQue.indexOf(obj);
			if(index == -1) return;
			_processQue.splice(index,1);
			_mixIsDirty = true
		}
		
		
		
		
		
		
		
		public function addGenerator( generator: IAudioProcessor):IAudioProcessor
		{
			if(_generators[generator.id]) return null;
			
			var i:int = 0;
			for(var key:Object in _generators)  i++;
			Object(generator).processIndex = i
			
			
			generator.activateFunction = activateGenerator;
			generator.deactivatedFunction = deactivateGenerator;
			generator.addEffectFunction = addChannelEffect;
			generator.removeEffectFunction = removeChannelEffect;
			_generators[generator.id] = generator;
			_channelEffects[generator.id] = new Dictionary();
			
			debug.numGenerators++;
			_mixIsDirty = true;
			return generator; 	
		}
		public function removeGenerator(obj:Object):void
		{
			
			if(obj is String) 
			{
				if(_generators[obj])
				{
					obj = _generators[obj]			
				}else
				{
					return;
				}
			}
			if(!_generators[obj.id]) return;
			if(_processQue.indexOf(obj) != -1) obj.deactivate();
			for each (var value:Object in _channelEffects[obj.id]) {
				value.deactivate();
			}
			delete _generators[obj.id];
			delete _channelEffects[obj.id];
			
			debug.numGenerators--;
		}
		public function activateGenerator(obj:Object):void
		{
			if(obj is String) 
			{
				if(_generators[obj])
				{
					obj = _generators[obj]; obj.activate();				
				}
				return;
			}
			if(!_generators[obj]) _generators[obj.id] = obj;
			var index:int = _processQue.indexOf(obj);
			if(index == -1)
			{
				var l:int = _processQue.length;
				var j:int = 0;
				while(j < l)
				{
					if(Object(_processQue[j]).hasOwnProperty("processIndex"))
					{
						if(Object(_processQue[j]).processIndex > obj.processIndex)
						{
							break;
						}
					}
					j++
				}
				_processQue.splice(j,0,obj);
			} 
			else return;
			for each (var value:Object in _channelEffects[obj.id]) {
				activateChannelEffect(value,obj.id);
			_mixIsDirty = true
			}
			_mixIsDirty = true
				
		}
		public function deactivateGenerator(obj:Object):void
		{
			if(obj is String) 
			{
				if(_generators[obj])
				{
					obj = _generators[obj]; obj.activate();				
				}
				return;
			} 		
			
			var i:int = 0;
			for each (var value:Object in _channelEffects[obj.id]) {
			  value.deactivate();
			  i++;
			}
			var index:int = _processQue.indexOf(obj);
			if(index != -1) _processQue.splice(index, 1);
			_mixIsDirty = true			 
		}
		
		
		
		
		// GOES INTO SUB CLASS
		
		public function getGenerator(id:String):IAudioProcessor
		{
			var gen:IAudioProcessor = _generators[id];
			return gen;
		}
		
		public function getEffect(id:String):IAudioProcessor{
			return IAudioProcessor(_masterEffects[id]);
		}
		
		
		
		
		
		
		
		public function get driver(): AudioDriver { return _driver; }
		public function get mixer() : Mixer { return _mixer; }
		public function set mixer(val:Mixer):void { mixer = val;}
		public function set debugger(val:Function):void { _debugFunction = val;}
		
		
		
	}
}
